﻿using System;
using System.Windows;
using System.Windows.Media;

namespace FAQ_UI
{
	public class MyGridView : MyAdapterView
	{
		internal static readonly DependencyProperty GridCountProperty = DependencyProperty.Register("GridCount", typeof(double), typeof(MyGridView), new UIPropertyMetadata(4.0, GridChange));
		internal static readonly DependencyProperty GridTypeProperty = DependencyProperty.Register("GridCountType", typeof(GridCountTypes), typeof(MyGridView), new UIPropertyMetadata(GridCountTypes.COUNT, GridChange));

		private static void GridChange(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			MyGridView g = (MyGridView)d;
			g.OnGridCountChange();
		}

		private void OnGridCountChange()
		{
			int newCount = getGridCount();
			notifyDataChanged();
			lastGridCount = newCount; ;
		}

		public enum GridCountTypes
		{
			WIDTH, COUNT
		}

		public double GridCount
		{
			get => (double)GetValue(GridCountProperty);
			set => SetValue(GridCountProperty, value);
		}

		private int lastGridCount = 0;

		public GridCountTypes GridCountType
		{
			get => (GridCountTypes)GetValue(GridTypeProperty);
			set => SetValue(GridTypeProperty, value);
		}

		public int getGridCount()
		{
			if (GridCountType == GridCountTypes.WIDTH)
			{
				double rw = isStackV() ? RenderSize.Width : RenderSize.Height;
				return (int)(rw / GridCount);
			}
			else if (GridCountType == GridCountTypes.COUNT) return (int)GridCount;
			throw new NotImplementedException();
		}

		public double getGridWidth()
		{
			double rw = isStackV() ? RenderSize.Width : RenderSize.Height;
			if (GridCountType == GridCountTypes.COUNT) return rw / GridCount;
			else if (GridCountType == GridCountTypes.WIDTH)
			{
				int c = (int)(rw / GridCount);
				return rw / c;
			}
			throw new NotImplementedException();
		}

		public double getMeasuredValue(UIElement v)
		{
			return isStackV() ? v.DesiredSize.Height : v.DesiredSize.Width;
		}

		public double getScrollBarWidth()
		{
			return 10;
		}

		protected override void fillList(double value)
		{
			if (RenderSize.Width == 0 || RenderSize.Height == 0 || getCount() == 0) return;
			fillListEnd(value);
			fillListStart(value);
		}

		protected override void OnRender(DrawingContext dc)
		{
			base.OnRender(dc);
		}

		protected override void OnRenderSizeChanged(System.Windows.SizeChangedInfo info)
		{
			mMaxValue = Double.MaxValue;
			int g = getGridCount();
			if (g != lastGridCount) OnGridCountChange();

			onScrollChange();
		}

		protected override void positionItems(double value)
		{
			if (Children.Count == 0) return;

			mDisplayOffset += value;
			double start = mDisplayOffset;
			if (StackOrientation == StackOrientations.BOTTOM) start += RenderSize.Height;
			if (StackOrientation == StackOrientations.RIGHT) start += RenderSize.Width;
			double maxW = HorizontalAlignment == System.Windows.HorizontalAlignment.Stretch ? RenderSize.Width : 0;
			double maxH = VerticalAlignment == System.Windows.VerticalAlignment.Stretch ? RenderSize.Height : 0;
			int gridCount = getGridCount();
			double gridWidth = getGridWidth(); ;
			for (int i = 0; i < Children.Count; i += gridCount)//列Index
			{
				double rowHeight = 0;
				for (int j = 0; j < gridCount; j++)//行Index
				{
					UIElement child = getChildAt(i + j);
					if (child == null) break;
					double childValue = getMeasuredValue(child);//w or h
					rowHeight = Math.Max(rowHeight, childValue);
					layoutChild(child, j * gridWidth, start, gridWidth, childValue);
					/*if (StackOrientation == StackOrientations.BOTTOM)
					{
						layoutChild(child, getBoundX(), start - childValue, getBoundX() + Math.Min(Math.Max(child.DesiredSize.Width, maxW), RenderSize.Width), start);
					}
					else if (StackOrientation == StackOrientations.RIGHT)
					{
						layoutChild(child, start - childValue, getBoundY(), start, getBoundY() + Math.Min(Math.Max(child.DesiredSize.Height, maxH), RenderSize.Height));
					}
					else if (StackOrientation == StackOrientations.TOP)
					{
						layoutChild(child, getBoundX(), getBoundY() + start, getBoundX() + Math.Min(Math.Max(child.DesiredSize.Width, maxW), RenderSize.Width), getBoundY() + start + childValue);
					}
					else if (StackOrientation == StackOrientations.LEFT)
					{
						layoutChild(child, getBoundX() + start, getBoundY(), getBoundX() + start + childValue, getBoundY() + Math.Min(Math.Max(child.DesiredSize.Height, maxH), RenderSize.Height));
					}*/
				}
				if (isStackInv()) start -= rowHeight;
				else start += rowHeight;
			}
		}

		protected override void removeNonVisibleItems(double dy)
		{
			int col = getGridCount();
			if (Children.Count >= col && col > 0)
			{
				double content = isStackV() ? RenderSize.Height : RenderSize.Width;
				double maxEnd = 0, maxValue = 0;
				for (int i = 0; i < col; i++)
				{
					UIElement child = getChildAt(i);
					maxEnd = Math.Max(maxEnd, getChildEnd(child));
					maxValue = Math.Max(maxValue, getMeasuredValue(child));
				}
				if (maxEnd + dy < 0)
				{
					if (isStackInv()) mDisplayOffset -= maxValue;
					else mDisplayOffset += maxValue;
					for (int i = 0; i < col; i++)
					{
						UIElement child = getChildAt(0);
						mRecycler.put(this, child);
						mStartViewIndex++;
					}
				}
				double minStart = Double.MaxValue;
				maxValue = 0;
				int a = (Children.Count - 1) / col;
				int cStart = a * col, cEnd = cStart + col;
				cEnd = Math.Min(cEnd, Children.Count);
				for (int i = cStart; i < cEnd; i++)
				{
					UIElement child = getChildAt(i);
					minStart = Math.Min(minStart, getChildStart(child));
					maxValue = Math.Max(maxValue, getMeasuredValue(child));
				}
				if (minStart + dy > content)
				{
					for (int i = cStart; i < cEnd; i++)
					{
						UIElement child = getChildAt(cStart);
						mRecycler.put(this, child);
						mEndViewIndex--;
					}
				}
			}
		}

		private void fillListEnd(double value)//edge为本行最大值
		{
			UIElement child = getChildAt(Children.Count - 1);
			double mValue = isStackV() ? RenderSize.Height : RenderSize.Width;
			double edge;
			int col = getGridCount();
			if (col == 0) return;
			if (child == null) edge = 0;
			else if (Children.Count % col == 0) edge = getChildMaxEnd(child);
			else edge = getChildStart(child);
			while (edge + value < mValue && mEndViewIndex < getCount())
			{
				for (int i = 0; i < col && mEndViewIndex < getCount(); i++)//补满一行
				{
					addViewHolderEnd();
				}
				//测量本行最大高度
				int row = (Children.Count - 1) / col;
				int cStart = row * col;
				int cEnd = cStart + col;
				cEnd = Math.Min(cEnd, Children.Count);
				double maxValue = 0;
				for (int i = cStart; i < cEnd; i++)
				{
					maxValue = getMeasuredValue(getChildAt(i));
				}
				if (isStackInv()) edge -= maxValue;
				else edge += maxValue;
				if (mEndViewIndex >= getCount())
				{
					if (isStackInv()) mMaxValue = mCurrentValue - edge;
					else mMaxValue = mCurrentValue + edge - mValue;
				}
				else mMaxValue = Double.MaxValue;
				if (mMaxValue < 0)
				{
					mMaxValue = 0;
				}
			}
		}

		private void fillListStart(double value)
		{
			UIElement child = getChildAt(0);
			double edge;
			int col = getGridCount();
			if (child == null) edge = 0;
			else edge = getChildStart(child);
			while (edge + value > 0 && mStartViewIndex >= 0)
			{
				for (int i = 0; i < col && mStartViewIndex >= 0; i++)//补满一行
				{
					addViewHolderStart();
				}
				//测量本行最大高度
				double maxValue = 0;
				for (int i = 0; i < col; i++)
				{
					maxValue = getMeasuredValue(getChildAt(i));
				}
				if (isStackInv())
				{
					edge += maxValue;
					mDisplayOffset += maxValue;
				}
				else
				{
					edge -= maxValue;
					mDisplayOffset -= maxValue;
				}
			}
		}

		private double getChildEnd(UIElement child)
		{
			return getChildStart(child) + (isStackV() ? child.RenderSize.Height : child.RenderSize.Width);
		}

		private double getChildMaxEnd(UIElement child)
		{
			int index = Children.IndexOf(child);
			double max = 0;
			int gc = getGridCount();
			int row = index / gc;
			int cStart = row * gc, cEnd = cStart + gc;
			if (cEnd > Children.Count) cEnd = Children.Count;
			for (int i = cStart; i < cEnd; i++)
			{
				max = Math.Max(max, getChildEnd(child));
			}
			return max;
		}

		private int getColStart(int index)
		{
			int gc = getGridCount();
			int row = index / gc;
			return row * gc;
		}

		private int getColEnd(int index)
		{
			int gc = getGridCount();
			int cEnd = getColStart(index) + gc;
			if (cEnd > Children.Count) return Children.Count;
			return cEnd;
		}

		private double getChildMinStart(UIElement child)
		{
			int index = Children.IndexOf(child);
			double max = 0;
			int gc = getGridCount();
			int row = index / gc;
			int cStart = row * gc, cEnd = cStart + gc;
			if (cEnd > Children.Count) cEnd = Children.Count;
			for (int i = cStart; i < cEnd; i++)
			{
				max = Math.Min(max, getChildStart(child));
			}
			return max;
		}

		private double getChildStart(UIElement child)
		{
			GeneralTransform gt = child.TransformToAncestor(this);
			Point point = gt.Transform(new Point(0, 0));
			double x = point.X;
			double y = point.Y;
			return isStackV() ? y : x;
		}

		private bool isChildNonVisible(UIElement child)
		{
			double content = isStackV() ? RenderSize.Height : RenderSize.Width;
			double start = getChildStart(child);
			double end = getChildEnd(child);
			if (start == 0 && end == 0) return true;
			return start > content || end < 0;
		}

		private void layoutChild(UIElement child, double x, double y, double w, double h)
		{
			Rect rect2 = new Rect(x, y, w, h);
			child.Arrange(rect2);
		}
	}
}